package com.hanshg.cherry.service.impl;

import com.hanshg.cherry.handler.MyMimeMessageHelper;
import com.hanshg.cherry.model.SysMail;
import com.hanshg.cherry.service.mail.MailService;
import com.hanshg.cherry.service.mail.SysMailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import oshi.util.Util;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * @author 柠檬水
 */
@Service
@Slf4j
public class MailerServiceImpl implements MailService {

    @Autowired
    private SysMailService sysMailService;

    /**
     * 邮件发送的对象，用于邮件发送
     */
    private JavaMailSenderImpl mailSender;

    /**
     * 获取设置mail信息
     */
    private SysMail sysMail;

    /**
     * 模板引擎解析对象，用于解析模板
     */
    @Autowired
    private TemplateEngine templateEngine;

    /**
     * 异步发送邮件
     *
     * @param subject     主题
     * @param content     内容
     * @param toWho       需要发送的人
     * @param ccPeoples   需要抄送的人
     * @param bccPeoples  需要密送的人
     * @param attachments 需要附带的附件
     */
    @Override
    public void sendSimpleTextMailActual(String subject, String content, String[] toWho, String[] ccPeoples, String[] bccPeoples, List<Map<String, String>> attachments, boolean isNet) {
        //检验参数：邮件主题、收件人、邮件内容必须不为空才能够保证基本的逻辑执行
        if (subject == null || toWho == null || toWho.length == 0 || content == null) {
            log.error("邮件-> {} 无法继续执行，因为缺少基本的参数：邮件主题、收件人、邮件内容", subject);
            throw new RuntimeException("模板邮件无法继续发送，因为缺少必要的参数！");
        }

        log.info("开始发送简单文本邮件：主题->{}，收件人->{}，抄送人->{}，密送人->{}，附件->{}", subject, toWho, ccPeoples, bccPeoples, attachments);
        try {
            //附件处理，需要处理附件时，需要使用二进制信息，使用 MimeMessage 类来进行处理
            mailSender = getJavaMailSender();
            Util.sleep(1000);
            //附件处理需要进行二进制传输
            MimeMessage mimeMessage = mailSender.createMimeMessage();
            MyMimeMessageHelper helper = new MyMimeMessageHelper(mimeMessage, true, mailSender.getDefaultEncoding());
            //设置邮件的基本信息：这些函数都会在后面列出来
            boolean continueProcess = handleBasicInfo(helper, subject, content, toWho, ccPeoples, bccPeoples, true);
            //如果处理基本信息出现错误
            if (!continueProcess) {
                log.error("邮件发送失败，请联系樱桃小姐姐: 主题->{}", subject);
                throw new RuntimeException("邮件基本信息出错，请联系樱桃小姐姐");
            }
            if (attachments != null && attachments.size() > 0) {
                //处理附件后发送
                handleAttachment(helper, subject, attachments, isNet);
            }
            //发送该邮件
            mailSender.send(mimeMessage);
            log.info("发送邮件成功: 主题->{}", subject);
        } catch (MessagingException e) {
            e.printStackTrace();
            log.error("发送邮件失败: 主题->{}", subject);
            throw new RuntimeException("邮件发送失败，请联系樱桃小姐姐");
        } catch (Exception e) {
            e.printStackTrace();
            log.error("发送邮件失败: 主题->{}", subject);
            throw new RuntimeException("邮件发送失败，请联系樱桃小姐姐");
        }
    }

    /**
     * 处理二进制邮件的基本信息，比如需要带附件的文本邮件、HTML文件、图片邮件、模板邮件等等
     *
     * @param mimeMessageHelper：二进制文件的包装类
     * @param subject：邮件主题
     * @param content：邮件内容
     * @param toWho：收件人
     * @param ccPeoples：抄送人
     * @param bccPeoples：暗送人
     * @param isHtml：是否是HTML文件，用于区分带附件的简单文本邮件和真正的HTML文件
     * @return ：返回这个过程中是否出现异常，当出现异常时会取消邮件的发送
     */
    public boolean handleBasicInfo(MyMimeMessageHelper mimeMessageHelper, String subject, String content, String[] toWho, String[] ccPeoples, String[] bccPeoples, boolean isHtml) {
        try {
            //设置必要的邮件元素
            //设置发件人
            mimeMessageHelper.setFrom(sysMail.getNickname() + '<' + mailSender.getUsername() + '>');
            //设置邮件的主题
            mimeMessageHelper.setSubject(subject);
            //设置邮件的内容，区别是否是HTML邮件
            mimeMessageHelper.setText(content, isHtml);
            //设置邮件的收件人
            mimeMessageHelper.setTo(toWho);
            //设置非必要的邮件元素，在使用helper进行封装时，这些数据都不能够为空
            if (ccPeoples != null) {
                //设置邮件的抄送人
                mimeMessageHelper.setCc(ccPeoples);
            }
            if (bccPeoples != null) {
                //设置邮件的密送人
                mimeMessageHelper.setBcc(bccPeoples);
            }
            return true;
        } catch (MessagingException e) {
            log.error("邮件基本信息出错->{}", subject);
        }
        return false;
    }

    /**
     * 用于处理附件信息，附件需要 MimeMessage 对象
     *
     * @param mimeMessageHelper：处理附件的信息对象
     * @param subject：邮件的主题，用于日志记录
     * @param attachmentFilePaths：附件文件的路径，该路径要求可以定位到本机的一个资源
     */
    public void handleAttachment(MyMimeMessageHelper mimeMessageHelper, String subject, List<Map<String, String>> attachmentFilePaths, boolean isNet) throws Exception {
        //判断是否需要处理邮件的附件
        if (attachmentFilePaths != null && attachmentFilePaths.size() > 0) {
            for (Map<String, String> attachmentFilePath : attachmentFilePaths) {
                Map<String, String> map = attachmentFilePath;
                if (isNet) {
                    URL url = new URL(map.get("annex"));
                    //添加网络资源附件
                    try {
                        //添加附件
                        mimeMessageHelper.addAttachment(map.get("fileName"), url);
                    } catch (MessagingException e) {
                        log.error("邮件->{} 添加附件->{} 出现异常->{}", subject, map.get("annex"), e.getMessage());
                        throw new RuntimeException("邮件添加附件出现异常，请联系樱桃小姐姐");
                    }
                } else {
                    FileSystemResource resource = new FileSystemResource(new File(map.get("annex")));
                    if (!resource.exists()) {
                        log.warn("邮件->{} 的附件->{} 不存在！", subject, attachmentFilePath);
                        continue;
                    }
                    //获取资源的名称
                    String fileName = resource.getFilename();
                    try {
                        //添加附件
                        mimeMessageHelper.addAttachment(fileName, resource);
                    } catch (MessagingException e) {
                        log.error("邮件->{} 添加附件->{} 出现异常->{}", subject, map.get("annex"), e.getMessage());
                        throw new RuntimeException("邮件添加附件出现异常，请联系樱桃小姐姐");
                    }
                }
            }
        }
    }

    /**
     * 创建邮件链接信息
     *
     * @return
     */
    private JavaMailSenderImpl getJavaMailSender() {
        sysMail = sysMailService.getSysMail();
        JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
        //设置服务器信息
        javaMailSender.setHost(sysMail.getHost());
        javaMailSender.setPassword(sysMail.getPassword());
        javaMailSender.setPort(sysMail.getPort());
        javaMailSender.setUsername(sysMail.getUsername());
        javaMailSender.setProtocol(sysMail.getProtocol());
        javaMailSender.setDefaultEncoding(sysMail.getEncoding());
        Properties properties = new Properties();
        properties.setProperty("mail.smtp.timeout", String.valueOf(sysMail.getTimeout()));
        properties.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        properties.setProperty("mail.smtp.auth", "true");
        properties.setProperty("mail.smtp.ssl.enable", "true");
        javaMailSender.setJavaMailProperties(properties);
        return javaMailSender;
    }
}
